﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Entity;
using System.Linq;
using System.Text;
using DG.Common.Patterns.MVVM;

namespace DG.Common.EF.Reflection
{
    public interface IEntityViewerProperty
    {
        EntityPropertyConceptualInfos EntityPropertyConceptualInfo { get; set; }
        bool IsReadOnly { get; }
        string Name { get; }
        string DisplayName { get; }
        string Description { get; }
        string Prompt { get; }
        object Value { get; set; }
        object[] NavigationCollection { get; }
        object[] EnumCollection { get; }
    }

    public class EntityViewerProperty : IEntityViewerProperty
    {
        private bool? _isReadOnly;
        public EntityViewerModel EntityViewerModel { get; set; }
        public EntityPropertyConceptualInfos EntityPropertyConceptualInfo { get; set; }
        
        public bool IsReadOnly
        {
            get
            {
                return _isReadOnly ?? EntityPropertyConceptualInfo.IsKey;
            }
        }
        public string Name
        {
            get
            {
                return EntityPropertyConceptualInfo.Name;
            }
        }
        public string DisplayName
        {
            get
            {
                return EntityPropertyConceptualInfo.DisplayName;
            }
        }
        public string Description
        {
            get
            {
                return EntityPropertyConceptualInfo.Description;
            }
        }
        public string Prompt
        {
            get
            {
                return EntityPropertyConceptualInfo.Prompt;
            }
        }
        public object Value
        {
            get
            {
                return EntityPropertyConceptualInfo.Property.GetValue(EntityViewerModel.Entity, null);
            }
            set
            {
                if (IsReadOnly) throw new InvalidOperationException("Property is ReadOnly");
                if (value != null && !EntityPropertyConceptualInfo.Type.IsInstanceOfType(value))
                {
                    try
                    {
                        value = Convert.ChangeType(value, EntityPropertyConceptualInfo.Type);
                    }
                    catch (Exception)
                    {
                        value = Value;   
                    }                    
                }
                EntityPropertyConceptualInfo.Property.SetValue(EntityViewerModel.Entity, value, null);
            }
        }

        public object[] NavigationCollection
        {
            get
            {
                var q = EntityViewerModel.Context.Set(EntityPropertyConceptualInfo.Type);
                q.Load();

                return q.Project().ToArray();
            }
        }
        public object[] EnumCollection
        {
            get
            {
                var q = Enum.GetValues(EntityPropertyConceptualInfo.Property.PropertyType);
                return q.Cast<object>().ToArray();
            }
        }

        public EntityViewerProperty(EntityViewerModel entityViewerModel, EntityPropertyConceptualInfos entityPropertyConceptualInfo
            , bool? isReadOnly = null)
        {
            _isReadOnly = isReadOnly;
            EntityViewerModel = entityViewerModel;
            EntityPropertyConceptualInfo = entityPropertyConceptualInfo;

        }
    }

    public class EntityViewerPropertyFilter : NotifyViewModel, IEntityViewerProperty
    {
        public EntityViewerModel EntityViewerModel { get; set; }
        public EntityPropertyConceptualInfos EntityPropertyConceptualInfo { get; set; }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public string Name
        {
            get
            {
                return EntityPropertyConceptualInfo.Name;
            }
        }
        public string DisplayName
        {
            get
            {
                return EntityPropertyConceptualInfo.DisplayName;
            }
        }
        public string Description
        {
            get
            {
                return EntityPropertyConceptualInfo.Description;
            }
        }
        public string Prompt
        {
            get
            {
                return EntityPropertyConceptualInfo.Prompt;
            }
        }

        private object _value;
        public object Value
        {
            get
            {
                return _value;
            }
            set
            {
                if (value != null && !EntityPropertyConceptualInfo.Type.IsInstanceOfType(value))
                {
                    try
                    {
                        value = Convert.ChangeType(value, EntityPropertyConceptualInfo.Type);
                    }
                    catch (Exception)
                    {
                        value = Value;
                    }
                }
                _value = value;
                OnPropertyChanged("Value");
                
            }
        }

        public object[] NavigationCollection
        {
            get
            {
                var q = Context.Set(EntityPropertyConceptualInfo.Type);
                q.Load();

                return q.Project().ToArray();
            }
        }
        public object[] EnumCollection
        {
            get
            {
                var q = Enum.GetValues(EntityPropertyConceptualInfo.Property.PropertyType);
                return q.Cast<object>().ToArray();
            }
        }

        public EntityViewerPropertyFilter(IDbContext ctx, EntityPropertyConceptualInfos entityPropertyConceptualInfo)
        {
            Context = ctx;
            EntityPropertyConceptualInfo = entityPropertyConceptualInfo;
        }

        public IDbContext Context { get;private set; }
    }
}
